package fr.jade.util;

import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.objectweb.fractal.api.Component;
import org.objectweb.fractal.api.Interface;
import org.objectweb.fractal.api.NoSuchInterfaceException;
import org.objectweb.fractal.api.control.ContentController;
import org.objectweb.fractal.api.control.NameController;
import org.objectweb.fractal.api.control.SuperController;
import org.objectweb.fractal.api.type.InterfaceType;

import fr.jade.fraclite.api.control.GenericAttributeController;
import fr.jade.fraclite.api.control.NoSuchAttributeException;
import fr.jade.util.log.Logger;

public class FractalNavigator {

  // Filters
  
  public static Set<Component> nameStartWithFilter(Set<Component> cmps, String prefix) {
    Set<Component> l = new HashSet<Component>();
    for (Component cmp : cmps) {
      try {
        NameController nc = FractalUtil.getNameController(cmp);
        if (nc.getFcName().startsWith(prefix))
          l.add(cmp);
      } catch (NoSuchInterfaceException ignored) {
      }
    }
    FractalPrinter.printComponentSet("nameStartWith"+prefix+":", l);
    return l;
  }

  public static Set<Component> withAttributeNameFilter(Set<Component> cmps, String attName) {
    Set<Component> l = new HashSet<Component>();
    for (Component cmp : cmps) {
      try {
        GenericAttributeController ac = FractalUtil.getAttributeController(cmp);
        try {
          ac.getAttribute(attName);
          l.add(cmp);
        } catch (NoSuchAttributeException ignored) {
        }
      } catch (NoSuchInterfaceException ignored) {
      }
    }
    return l;
  }

  // Getters
  
  public static Set<Component> getParents(Component cmp) {
    Set<Component> parents = new HashSet<Component>();
    try {
      SuperController sc = FractalUtil.getOptSuperController(cmp);

      for (Component father : sc.getFcSuperComponents()) {
        parents.add(father);
      }
    } catch (NoSuchInterfaceException ignored) {
      //ignored.printStackTrace();
    }
    return parents;
  }

  public static Component getChildByName(Component cmp, String name) throws NoSuchInterfaceException {
    ContentController cc = FractalUtil.getOptContentController(cmp);
    Component child = null;
    for (Component sub : cc.getFcSubComponents()) {
      if (FractalNavigator.getName(sub).equals(name)) {
        child = sub;
        break;
      }
    }
    return child;
  }

  public static Component getParentByName(Component cmp, String name) throws NoSuchInterfaceException {
    Component result = null;
    for (Component parent : getParents(cmp)) {
      if (FractalNavigator.getName(parent).equals(name)) {
        result = parent;
        break;
      }
    }
    return result;
  }
  
  /**
   * utility method to find a subcomponent from a component root. 
   * @param c the component root
   * @param cmpPath the full path name of the component to find.
   * @param findChild true finds in childs, false finds in fathers
   * @return the component or null if the cmp is not found.
   * 
   * example : 
   * if C0(C1,C2(C4,C5),C3)
   * find(C0,"C2/C5") will return the ref of C5
   * @throws NoSuchInterfaceException 
   */
  public static Component find(Component c, String sub, boolean findChild) throws NoSuchInterfaceException {
    int idx;
    String cmp = null;
    String newPath;
    Component subc = null;

    idx = sub.indexOf('/');
    if (idx != -1) {
      // find the cmp and follow the path
      cmp = sub.substring(0, idx);
      newPath = sub.substring(idx + 1);
      if(findChild){
        subc = getChildByName(c, cmp);
      }else{
        subc = getParentByName(c, cmp);
      }
      if (subc == null)
        return null;
      return find(subc, newPath, findChild);
    } else {
      // find the subcmp
      if(findChild){
        return getChildByName(c, sub);
      }else{
        return getParentByName(c, sub);
      }
    }
  }
  
  public static Component getNode(Component cmp) throws JadeException {
    try {
      return (Component)CollectionUtil.checkAndGetSingleton(withAttributeNameFilter(getParents(cmp), "hostName"));
    } catch (IndexOutOfBoundsException e) {
      throw new JadeException("No node component for " + cmp, e);
    }
  }

  /**
   * Test if the component passed as parameter has a client itf whose signature
   * correspond to the signature passed as parameter.
   * @param cmp the component to be tested
   * @param signature the signature of the itf
   * @return an array of interface object having this signature 
   * (these interfaces are client/external) or null.  
   * @throws NoSuchInterfaceException
   */
  public static List<Interface> getClientInterfacesBySignature(Component cmp, String signature) throws NoSuchInterfaceException {

    List<Interface> res = new LinkedList<Interface>();

      for (Object o : cmp.getFcInterfaces()) {
        Interface i = (Interface)o;
        InterfaceType it = (InterfaceType)i.getFcItfType();
        if (it.isFcClientItf() && it.getFcItfSignature().equals(signature)) {
            res.add(i);
          }
      }
      return res;
    
  }
  
  public static Map<String, String> getAttributes(Component cmp) throws NoSuchInterfaceException{
    Map<String, String> atttributes = new HashMap<String, String>();
    
    GenericAttributeController gac = FractalUtil.getAttributeController(cmp);
    String[] atts = gac.listFcAtt();
    assert atts != null : "atts cannot be null";
    for(String key : atts){
      try {
        String value = gac.getAttribute(key);
        atttributes.put(key, value);
      } catch (NoSuchAttributeException e) {
        // should not occur
        Logger.println(e);
      }
      
    }
    
    return atttributes;
  }

  public static String getName(Component cmp_E) {
    String name = null;
    try {
      name = FractalUtil.getNameController(cmp_E).getFcName();
    } catch (NoSuchInterfaceException ignored) { }
    assert name != null : "Component must have a name controller";
    return name;
  }

  public static String getOptName(Component cmp_E) throws NoSuchInterfaceException{
    return FractalUtil.getNameController(cmp_E).getFcName();
  }
  
  public static Set<Interface> getClientInterfaces(Component cmp) {
    assert cmp != null : "Component cannot be null";
    Set<Interface> result = new HashSet<Interface>();
    for(Object obj : cmp.getFcInterfaces()){
      Interface i = (Interface)obj;
      InterfaceType it = (InterfaceType)i.getFcItfType();
      if(it.isFcClientItf()) result.add(i);
    }
    try{
      ContentController cc = FractalUtil.getOptContentController(cmp);
      for(Object obj : cc.getFcInternalInterfaces()){
        Interface i = (Interface)obj;
        InterfaceType it = (InterfaceType)i.getFcItfType();
        if(it.isFcClientItf()) result.add(i);
      }
    }catch(NoSuchInterfaceException ignored){}
    return result;
  }
  
  public static String[] listCollectionBinding(Component comp, String bindingPrefix) throws JadeException {
    String[] bindings;

    try {
      bindings = FractalUtil.getBindingController(comp).listFc();
    } catch (NoSuchInterfaceException e) {
      throw new JadeException("No binding controller", e);
    }

    HashSet<String> set = new HashSet<String>();
    for (int i = 0; i < bindings.length; ++i)
      if (bindings[i].startsWith(bindingPrefix))
        set.add(bindings[i]);

    return (String[]) set.toArray(new String[set.size()]);
  }

  public static Set<Component> getSibling(Component cmp) {
    //Logger.println("Sibling of " + getName(cmp));
    Set<Component> sibling = getSiblingOrSelf(cmp);
    sibling.remove(cmp);

    return sibling;
  }

  public static Set<Component> getSiblingOrSelf(Component cmp) {

    Set<Component> sibling = new HashSet<Component>();

    for (Component father : getParents(cmp)) {
      //Logger.println("father "+getName(father));
      try {
        ContentController cc = FractalUtil.getOptContentController(father);
        for (Component sub : cc.getFcSubComponents()) {
          if (!sibling.contains(sub)){
            //Logger.println(getName(sub)+"(sibling)"+sub.hashCode()+" - "+((Interface)sub).getFcItfGUId());
            sibling.add(sub);
          }else{
            //Logger.println(getName(sub)+"(duplicate)"+sub.hashCode()+" - "+((Interface)sub).getFcItfGUId());
          }
        }
      } catch (NoSuchInterfaceException ignored) {
      }
    }
    //FractalPrinter.printComponentSet("Sibling :", sibling);
    return sibling;
  }
}
